//****************************************Copyright (c)***********************************//
//原子哥在线教学平台：www.yuanzige.com
//技术支持：www.openedv.com
//淘宝店铺：http://openedv.taobao.com 
//关注微信公众平台微信号："正点原子"，免费获取ZYNQ & FPGA & STM32 & LINUX资料。
//版权所有，盗版必究。
//Copyright(C) 正点原子 2018-2028
//All rights reserved	                               
//----------------------------------------------------------------------------------------
// File name:           uart_rx
// Last modified Date:  2022/7/11 14:20:36
// Last Version:        V1.1
// Descriptions:        uart_demo串口接收模块
//----------------------------------------------------------------------------------------
// Created by:          正点原子
// Created date:        2022/7/11 9:16:39
// Version:             V1.0
// Descriptions:        The original version
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module uart_rx(
	input					sys_clk,        //系统时钟
	input					sys_rst_n,      //复位，低电平有效
	
	input					uart_rx,        //数据接收端    

	output	reg			    rx_done,        //接收完成信号
	output	reg	 [7 :0]	    rxd_data        //接收到的数据
);

//reg define
reg				uart_rx_d0;                 //rx一级寄存器
reg				uart_rx_d1;                 //rx二级寄存器                                
reg			    rx_flag;                    //rx标志信号
reg		[3 :0]	rx_cnt;                     //rx比特计数器
reg	    [15:0] 	baud_cnt;                   //波特率计数器
reg		[7 :0]	rx_data;                    //接收数据暂存器

//wire define
wire	        rx_start;                   //接收开始信号

//*****************************************************
//**                    main code
//*****************************************************
//捕获接收端口下降沿(起始位)，得到一个时钟周期的脉冲信号
assign rx_start = (~uart_rx_d0) & uart_rx_d1;

//对uart接收端口进行二级缓存主要用于消除亚稳态
always @ (posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		uart_rx_d0 <= 1'b0;
		uart_rx_d1 <= 1'b0;
	end
	else begin
		uart_rx_d0 <= uart_rx;
		uart_rx_d1 <= uart_rx_d0;
	end
end

//当脉冲进入接收端的开始信号，进入接收过程
always @ (posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		rx_flag <= 1'b0;
	else 
		if(rx_start)                //检测到起始位
			rx_flag <= 1'b1;        //进入接收过程，标志位rx_flag拉高，
		else if((rx_cnt == 4'd9) && (baud_cnt == (25'd25_000_000/17'd115_200) - 1'b1))
			rx_flag <= 1'b0;        //接收过程的一半时拉低的标志位
		else
		    rx_flag <= rx_flag;    
end

//进入接收过程后，启动波特率计数器
always @ (posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		baud_cnt <= 16'd0;
	else if(rx_flag)                    //处于接收过程
		if(baud_cnt < (26'd50_000_000 / 17'd115_200 - 1'b1))
			baud_cnt <= baud_cnt + 1'b1;
		else
			baud_cnt <= 16'd0;          //对系统时钟计数达一个波特率周期后清零
	else 
		baud_cnt <= 16'd0;              //接收过程结束，计数器清零
end

//进入接收过程后，启动比特位数计数器
always @(posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n)
		rx_cnt <= 4'd0;
	else if(rx_flag)                    //处于接收过程
		if(baud_cnt == (26'd50_000_000 / 17'd115_200 - 1'b1))  //对系统时钟计数达一个波特率周期
			rx_cnt <= rx_cnt + 1'b1;    //此时接收数据计数器加1
		else
			rx_cnt <= rx_cnt;
	else
		rx_cnt <= 4'd0;                 //接收过程结束，计数器清零
end

//根据接收计数器来寄存接收端口的数据
always @ (posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		rx_data <= 8'd0;
	end
	else if(rx_flag && (baud_cnt == 25'd25_000_000 / 17'd115_200)) begin       
			case(rx_cnt)                         //处于接收状态，且时钟计数器到中间位置
				4'd1: rx_data[0] <= uart_rx;     //寄存数据位最低
				4'd2: rx_data[1] <= uart_rx;
				4'd3: rx_data[2] <= uart_rx;
				4'd4: rx_data[3] <= uart_rx;
				4'd5: rx_data[4] <= uart_rx;
				4'd6: rx_data[5] <= uart_rx;
				4'd7: rx_data[6] <= uart_rx;
				4'd8: rx_data[7] <= uart_rx;     //寄存数据位最高
				default : ;
			endcase
	end
end

//将接收好的数据在收到有效标志位后传送到接收端的数据
always @ (posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) begin
		rx_done <= 1'b0;
		rxd_data <= 8'd0;
	end
	else if(rx_cnt == 4'd9) begin           //接收数据计数器计数到停止位时
		rxd_data <= rx_data;                //寄存输出接收到的数据
		rx_done <= 1'b1;                    //并将接收完成标志位拉高
	end
	else begin
		rxd_data <= rxd_data;
		rx_done <= 1'b0;
	end
end

endmodule